<?php
// +----------------------------------------------------------------------
// | Media媒体服务服务层
// +----------------------------------------------------------------------
// | 主要服务于Media媒体数据处理
// +----------------------------------------------------------------------
// | 错误编码头 210xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\CosMediaModel;
use common\model\MediaModel;
use common\model\MediaServerModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\model\RelationModel;
use Ws\Http\Request;
use Ws\Http\Request\Body;

class MediaService
{
    protected $mediaMap = [

    ];
    protected $_headers = [
        'Accept' => 'application/json',
        'content-type' => 'application/json'
    ];

    // 错误信息
    protected $errorMsg = '';

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->mediaMap = ["media" => model(MediaModel::class), "cos_media" => model(CosMediaModel::class)];;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->errorMsg;
    }


    /**
     * 远程请求数据
     * @param $data
     * @param $url
     * @return bool
     */
    protected function postData($data, $url)
    {
        $http = Request::create();
        $body = Body::json($data);
        $responseData = $http->post($url, $this->_headers, $body);
        if ($responseData->code === 200) {
            if ($responseData->body->status === 200) {
                return $responseData->body->data;
            } else {
                $this->errorMsg = $responseData->body->message;
                return false;
            }
        } else {
            $this->errorMsg = 'Media server exception. ';
            return false;
        }
    }

    /**
     * 上传媒体文件
     * @param $file
     * @return array|bool
     */
    public function uploadMedia($file)
    {
        try {
            $mediaUploadServer = $this->getMediaUploadServer();
            $data = [
                'token' => $mediaUploadServer['token'],
                'size' => '250x140'
            ];
            $http = Request::create();
            $body = Body::multipart($data, [
                'Filedata' => $file
            ]);

            $responseData = $http->post($mediaUploadServer['upload_url'], ['Accept' => 'application/json'], $body);
            if ($responseData->code === 200) {
                if ($responseData->body->status === 200) {
                    $mediaData = object_to_array($responseData->body->data);
                    return ["media_data" => $mediaData, "media_server" => $mediaUploadServer];
                } else {
                    $this->errorMsg = $responseData->body->message;
                    return false;
                }
            } else {
                $this->errorMsg = 'Media server exception. ';
                return false;
            }
        } catch (\exception $e) {
            // 没有可用媒体服务器
            $this->errorMsg = 'Media server exception. ';
            return false;
        }
    }

    /**
     * 新增媒体数据
     * @param $data
     * @return array
     */
    public function addMediaData($data)
    {
        $mediaModel = model(MediaModel::class);
        // 添加新的媒体信息
        $resData = $mediaModel->addItem($data);
        if (!$resData) {
            // 添加媒体失败错误码 001
            throw_strack_exception($mediaModel->getError(), ErrorCode::MEDIA_ADD_FAILURE);
        } else {
            return $resData;
        }
    }

    /**
     * 修改媒体数据
     * @param $param
     * @return array
     */
    protected function modifyMediaData($param)
    {
        $mediaModel = model(MediaModel::class);
        $mediaId = $mediaModel->where([
            'md5_name' => $param['md5_name']
        ])->getField("id");

        if ($mediaId > 0) {
            // 更新已经存在的媒体信息
            $param['id'] = $mediaId;

            // 删除老的缩略图
            $this->clearMediaThumbnail(['id' => $mediaId], 'modify');

            // 修改缩略图数据
            $resData = $mediaModel->modifyItem($param);

            if (!$resData) {
                // 修改媒体失败错误码 002
                throw_strack_exception($mediaModel->getError(), ErrorCode::MEDIA_MODIFY_FAILURE);
            } else {
                return $resData;
            }
        } else {
            return $this->addMediaData($param);
        }
    }


    /**
     * 生成处理媒体添加数据
     * @param $data
     * @param $mediaServerParam
     * @return mixed
     * @throws \Exception
     */
    public function generateMediaData(&$data, $mediaServerParam = [])
    {
        $mediaData = $data;

        if (!empty($mediaServerParam)) {
            $mediaServerData = $mediaServerParam;
        } elseif (!empty($data['request_url'])) {
            // 利用media_data里的request_url来重置媒体服务器的request_url
            $mediaServerData['request_url'] = $data['request_url'];
        } else {
            if (empty($data['media_server_id'])) {
                // media_server_id 抛出异常
                throw new LogicException('media_server_id does not exist.', ErrorCode::MEDIA_SERVER_NOTE_EXIST);
            } else {
                // 获取当前media server数据
                $mediaServer = MediaService::getInstance();
                $mediaServerData = $mediaServer->getMediaServerData($data['media_server_id']);

                if (!$mediaServerData) {
                    // 无效的媒体服务器
                    throw new LogicException('Invalid media server.', ErrorCode::MEDIA_SERVER_INVALID);
                }
            }
        }
        $uploadType = $data['upload_type'] ?? 'media_service';
        $thumbUrl = '';
        $width = $data["width"] ?? '';
        $height = $data['height'] ?? '';
        $mediaSize = $data["size"] ?? '';
        $path = $data['path'] ?? '';
        $thumb = $data['thumb'] ?? '';
        $md5Name = $data['md5_name'] ?? '';
        $ext = $data['ext'] ?? '';

        switch ($data["type"]) {
            case 'video':
                // 视频保存原始尺寸
                $mediaSize = "{$width}x{$height}";
                if ($uploadType === 'media_service') {
                    $pathTrim = str_replace('/' . $md5Name . '/', '', $path);
                    $thumbUrl = $mediaServerData['request_url'] . '/' . $pathTrim . $thumb;
                } else {
                    $thumbUrl = $thumb;
                }
                break;
            case 'image':
                if ($uploadType === 'media_service') {
                    // 获取图片上传尺寸
                    $sizeArray = explode(',', $mediaSize);
                    $minSize = 999999;
                    $minSizeString = 'origin';
                    foreach ($sizeArray as $sizeItem) {
                        if ($sizeItem !== 'origin' && strpos($sizeItem, 'x') !== false) {
                            if (explode('x', $sizeItem)[0] < $minSize) {
                                $minSizeString = $sizeItem;
                            }
                        }
                    }
                    $thumbUrl = "{$mediaServerData['request_url']}/{$path}{$md5Name}_{$minSizeString}.{$ext}";
                } else {
                    $thumbUrl = $thumb;
                }
                break;
        }

        if (!array_key_exists("base_url", $mediaData) && isset($mediaServerData['request_url']) && isset($mediaData['path'])) {
            // 缩略图地址
            $mediaData["base_url"] = $mediaServerData['request_url'] . "/" . $mediaData['path'];
        }

        // 缩略图尺寸
        $data["size"] = $mediaSize;

        // 缩略图相对路径
        $data["thumb"] = $thumbUrl;

        // 缩略图参数
        $data["param"] = $mediaData;

        return $data;
    }

    /**
     * 添加或者更新media
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function saveMediaData($param)
    {
        // 生成 media data
        $param['media_data']['media_server_id'] = $param['media_server']['id'];
        $mediaAddData = $this->generateMediaData($param['media_data'], $param['media_server']);
        $resData = $this->modifyMediaData($mediaAddData);
        return $resData;
    }

    /**
     * 获取指定媒体信息
     * @param $filter
     * @return array
     */
    public function getMediaData($filter)
    {
        $mediaModel = model(MediaModel::class);
        $mediaItem = $mediaModel->findData(["filter" => $filter]);
        if (!empty($mediaItem)) {
            // 获取媒体服务器信息
            $mediaServerData = $this->getMediaServerItem($mediaItem['media_server_id']);
            $url = $mediaServerData['request_url'] . "/media/get?sign={$mediaServerData['token']}";
            // 获取媒体
            $postResult = $this->postData(['md5_name' => $mediaItem['md5_name']], $url);
            if (!$postResult) {
                return ['has_media' => 'no', 'param' => []];
            } else {
                $mediaItemData = object_to_array($postResult->param);
                $mediaItemData['base_url'] = $mediaServerData['request_url'] . "/" . "{$mediaItemData['path']}";
                if ($mediaItemData['type'] === 'image') {
                    $mediaItemData['size'] = explode(',', $mediaItemData['size']);
                }
                return ['has_media' => 'yes', 'param' => $mediaItemData];
            }
        } else {
            return ['has_media' => 'no', 'param' => []];
        }
    }

    /**
     * 直接获取缩略图
     * @param $filter
     * @return string
     */
    public function getMediaThumb($filter)
    {
        $mediaModel = model(MediaModel::class);
        $mediaItem = $mediaModel->findData(["filter" => $filter, 'fields' => 'thumb']);
        return !empty($mediaItem) ? $mediaItem['thumb'] : '';
    }

    /**
     * 获取多条媒体信息
     * @param $filter
     * @return array
     */
    public function getMediaSelectData($filter)
    {
        // 根据条件获取所有媒体信息
        $mediaModel = model(MediaModel::class);
        $mediaData = $mediaModel->selectData(["filter" => $filter]);

        // 根据media_server_id分组数据
        $mediaSelectData = [];

        // MD5 Name 数据映射字典
        $mediaMd5NameDict = [];

        foreach ($mediaData["rows"] as $mediaItem) {
            $mediaMd5NameDict[$mediaItem["md5_name"]] = $mediaItem;
            if (array_key_exists($mediaItem['media_server_id'], $mediaSelectData)) {
                array_push($mediaSelectData[$mediaItem['media_server_id']], $mediaItem["md5_name"]);
            } else {
                $mediaSelectData[$mediaItem['media_server_id']] = [$mediaItem["md5_name"]];
            }
        }

        $selectMediaData = ['has_media' => 'no', 'param' => []];
        // 批量查询媒体数据
        foreach ($mediaSelectData as $key => $item) {
            $resData = $this->selectMediaData($key, $item);
            if ($resData["has_media"] === 'yes') {
                // 把media id 重新写入媒体数据
                foreach ($resData["param"] as &$mediaItem) {
                    $mediaItem["media_id"] = $mediaMd5NameDict[$mediaItem["md5_name"]]["id"];
                }
                $selectMediaData['param'] = array_merge($selectMediaData['param'], $resData["param"]);
            }

            if (!empty($selectMediaData['param'])) {
                $selectMediaData['has_media'] = "yes";
            }
        }
        return $selectMediaData;
    }

    /**
     * 获取多个媒体信息
     * @param $mediaServiceId
     * @param $md5NameList
     * @return array
     */
    public function selectMediaData($mediaServiceId, $md5NameList)
    {
        if (!empty($mediaServiceId) && is_array($md5NameList)) {
            // 获取媒体服务器信息
            $mediaServerData = $this->getMediaServerItem($mediaServiceId);
            $url = $mediaServerData['request_url'] . "/media/select?sign={$mediaServerData['token']}";

            // 获取媒体
            $postResult = $this->postData(['md5_name' => join(",", $md5NameList)], $url);

            if (!$postResult) {
                return ['has_media' => 'no', 'param' => []];
            } else {
                $mediaListData = [];
                foreach ($postResult as $mediaIte) {
                    $mediaItemParam = object_to_array($mediaIte);
                    $mediaItemData = $mediaItemParam["param"];
                    $mediaItemData['base_url'] = $mediaServerData['request_url'] . "/" . "{$mediaItemData['path']}";
                    if ($mediaItemData['type'] === 'image') {
                        $mediaItemData['size'] = explode(',', $mediaItemData['size']);
                    }
                    $mediaItemData["media_id"] = $mediaItemParam["id"];
                    array_push($mediaListData, $mediaItemData);
                }
                return ['has_media' => 'yes', 'param' => $mediaListData];
            }
        } else {
            return ['has_media' => 'no', 'param' => []];
        }
    }

    /**
     * 批量清除缩略图
     * @param $param
     * @return array
     */
    public function batchClearMediaThumbnail($param)
    {
        if (strpos($param["link_id"], ",") === false && !array_key_exists("mode", $param)) {
            $result = $this->clearMediaThumbnail([
                "link_id" => $param["link_id"],
                "module_id" => $param["module_id"]
            ]);
            if (!$result) {
                throw_strack_exception($this->errorMsg, ErrorCode::MEDIA_ADD_FAILURE);
            }
        } else {
            // 查找所有媒体
            $mediaModel = model(MediaModel::class);
            if (array_key_exists("delete_media_ids", $param) && $param["delete_media_ids"] == "yes") {
                $filter = [
                    "id" => ["IN", $param["link_id"]],
                ];
            } else {
                $filter = [
                    "link_id" => ["IN", $param["link_id"]],
                    "module_id" => $param["module_id"]
                ];
            }

            // 查找相关媒体信息
            $mediaData = $mediaModel->selectData([
                "filter" => $filter
            ]);

            foreach ($mediaData["rows"] as $mediaItem) {
                $mediaSelectData = $mediaModel->selectData(["filter" => ["md5_name" => $mediaItem["md5_name"]]]);
                if ($mediaSelectData["rows"] == 0) {
                    try {
                        $mediaServerData = $this->getMediaServerItem($mediaItem['media_server_id']);
                        $url = $mediaServerData['request_url'] . "/media/remove?sign={$mediaServerData['token']}";
                        // 清除媒体
                        $this->postData(['md5_name' => $mediaItem['md5_name']], $url);
                    } catch (\Exception $e) {

                    }
                }
                $resData = $mediaModel->where(["id" => $mediaItem["id"]])->delete();
                if (!$resData) {
                    throw_strack_exception($mediaModel->getError(), ErrorCode::MEDIA_CLEAR_FAILURE);
                }
            }
        }

        return success_response('Successfully cleared the specified media! ');
    }

    /**
     * 清除指定媒体缩略图
     * @param $filter
     * @param string $mode
     * @return bool
     */
    public function clearMediaThumbnail($filter, $mode = 'delete')
    {
        $mediaModel = model(MediaModel::class);
        unset($filter['mode']);
        $mediaItem = $mediaModel->findData(["filter" => $filter]);
        if (!empty($mediaItem)) {
            $mediaData = $mediaModel->selectData(["filter" => ["md5_name" => $mediaItem['md5_name']]]);
            if ($mediaData["total"] == 0) {
                // 获取媒体服务器信息
                $mediaServerData = $this->getMediaServerItem($mediaItem['media_server_id']);
                $url = $mediaServerData['request_url'] . "/media/remove?sign={$mediaServerData['token']}";
                // 清除媒体
                $postResult = $this->postData(['md5_name' => $mediaItem['md5_name']], $url);
                if (is_bool($postResult)) {
                    return false;
                }
            }

            // 删除指定数据
            if ($mode === 'delete') {
                $mediaModel->where(["id" => $mediaItem["id"]])->delete();
            }
            return true;
        } else {
            $this->errorMsg = 'There is no media available for the current entity. ';
            return false;
        }
    }

    /**
     * 获取指定尺寸的媒体缩略图路径
     * @param $filter
     * @param $size
     * @return string
     */
    public function getSpecifySizeThumbPath($filter, $size)
    {
        $mediaData = $this->getMediaData($filter);
        if (!empty($mediaData['param'])) {
            $imgSize = 'origin';
            if ($mediaData['param']['type'] === 'image' && in_array($size, $mediaData['param']['size'])) {
                $imgSize = $size;
            }
            return $mediaData['param']['base_url'] . "{$mediaData['param']['md5_name']}_{$imgSize}.{$mediaData['param']['ext']}";
        } else {
            return '';
        }
    }

    /**
     * 新增媒体服务器
     * @param $param
     * @return array
     */
    public function addMediaServer($param)
    {
        $mediaServiceModel = model(MediaServerModel::class);
        $resData = $mediaServiceModel->addItem($param);
        if (!$resData) {
            // 添加媒体服务失败错误码 003
            throw_strack_exception($mediaServiceModel->getError(), ErrorCode::MEDIA_SERVER_ADD_FAILURE);
        } else {
            // 返回成功数据
            return success_response($mediaServiceModel->getSuccessMessage(), $resData);
        }
    }

    /**
     * 修改媒体服务器
     * @param $param
     * @return array
     */
    public function modifyMediaServer($param)
    {
        $mediaServiceModel = model(MediaServerModel::class);
        $resData = $mediaServiceModel->modifyItem($param);
        if (!$resData) {
            // 修改媒体服务失败错误码 004
            throw_strack_exception($mediaServiceModel->getError(), ErrorCode::MEDIA_SERVER_MODIFY_FAILURE);
        } else {
            // 返回成功数据
            return success_response($mediaServiceModel->getSuccessMessage(), $resData);
        }
    }

    /**
     * 删除媒体服务器
     * @param $param
     * @return array
     */
    public function deleteMediaServer($param)
    {
        $mediaServiceModel = model(MediaServerModel::class);
        $resData = $mediaServiceModel->deleteItem($param);
        if (!$resData) {
            // 删除媒体服务器失败错误码 005
            throw_strack_exception($mediaServiceModel->getError(), ErrorCode::MEDIA_SERVER_DELETE_FAILURE);
        } else {
            // 返回成功数据
            return success_response($mediaServiceModel->getSuccessMessage(), $resData);
        }
    }


    /**
     * 获取媒体服务器状态
     * @return array
     */
    public function getMediaServerStatus()
    {
        // 获取所有系统配置的媒体服务器
        $mediaServiceModel = model(MediaServerModel::class);
        $options = [
            'fields' => 'id,name,request_url,upload_url,access_key,secret_key'
        ];
        $mediaServerData = $mediaServiceModel->selectData($options);
        $mediaServerStatusList = [];
        foreach ($mediaServerData["rows"] as $serverItem) {
            $getServerStatus = check_http_code($serverItem["request_url"]);
            array_push($mediaServerStatusList, [
                'status' => $getServerStatus['http_code'],
                'connect_time' => $getServerStatus['connect_time'],
                'id' => $serverItem['id'],
                'name' => $serverItem['name'],
                'request_url' => $serverItem['request_url'],
                'upload_url' => $serverItem['upload_url'],
                'access_key' => $serverItem['access_key'],
                'secret_key' => $serverItem['secret_key']
            ]);
        }
        return $mediaServerStatusList;
    }

    /**
     * 获取媒体指定上传服务器配置信息
     * @return array|mixed
     */
    public function getMediaUploadServer()
    {
        $mediaServerStatusList = $this->getMediaServerStatus();
        if (!empty($mediaServerStatusList)) {
            // 找到连接最快的媒体服务器
            $fastConnectTime = 99999;
            $fastMediaServer = [];
            foreach ($mediaServerStatusList as $mediaServerItem) {
                if ($mediaServerItem['connect_time'] > 0 && $mediaServerItem['connect_time'] < $fastConnectTime) {
                    $fastConnectTime = $mediaServerItem['connect_time'];
                    $fastMediaServer = $mediaServerItem;
                }
            }

            if (!empty($fastMediaServer)) {
                $token = md5($fastMediaServer["access_key"] . $fastMediaServer["secret_key"]);
                return [
                    'id' => $fastMediaServer['id'],
                    'name' => $fastMediaServer['name'],
                    'request_url' => $fastMediaServer['request_url'],
                    'upload_url' => $fastMediaServer['upload_url'],
                    'token' => $token
                ];
            } else {
                throw_strack_exception('There is no media server available. ', ErrorCode::NO_MEDIA_SERVER_AVAILABLE);
            }
        } else {
            throw_strack_exception('There is no media server available. ', ErrorCode::NO_MEDIA_SERVER_AVAILABLE);
        }
    }

    /**
     * 获取指定媒体服务器数据
     * @param $mediaServerId
     * @return array|mixed
     */
    public function getMediaServerData($mediaServerId)
    {
        $mediaServiceModel = model(MediaServerModel::class);
        $serverItem = $mediaServiceModel->findData([
            "fields" => 'id,name,upload_url,request_url,access_key,secret_key',
            "filter" => [
                'id' => $mediaServerId
            ]
        ]);

        return $serverItem;
    }

    /**
     * 获取指定服务器信息
     * @param $mediaServerId
     * @return array
     */
    public function getMediaServerItem($mediaServerId)
    {

        $serverItem = $this->getMediaServerData($mediaServerId);
        if (!empty($serverItem)) {
            $getServerStatus = check_http_code($serverItem["request_url"]);
            if ($getServerStatus['connect_time'] > 0) {
                $token = md5($serverItem["access_key"] . $serverItem["secret_key"]);
                return [
                    'id' => $serverItem['id'],
                    'name' => $serverItem['name'],
                    'request_url' => $serverItem['request_url'],
                    'upload_url' => $serverItem['upload_url'],
                    'token' => $token
                ];
            } else {
                throw_strack_exception('The specified media server is not available. ', ErrorCode::SPECIFIED_MEDIA_SERVER_NOT_AVAILABLE);
            }
        } else {
            throw_strack_exception('The specified media server does not exist. ', ErrorCode::SPECIFIED_MEDIA_SERVER_NOT_EXIST);
        }
    }

    /**
     * 查询媒体
     * @param $filter
     * @param $fields
     * @return array|false|mixed|string
     */
    public function selectMedia($filter, $fields)
    {
        return model(MediaModel::class)->where($filter)->field($fields)->select();
    }

    /**
     * 查询media字典
     * @param $mediaIdList
     * @param $mediaFields
     * @return array
     */
    public function selectMediaParamMap($mediaIdList, $mediaFields = 'id,thumb,type,param')
    {
        $res = [];
        if (empty($mediaIdList)) {
            return $res;
        }
        $mediaDict = model(MediaModel::class)
            ->field($mediaFields)
            ->where(['id' => ['IN', join(',', $mediaIdList)]])
            ->cache(C('database.default_query_cache_time'))
            ->select();
        foreach ($mediaDict as $mediaItem) {
            if (!empty($mediaItem['param'])) {
                $mediaItem['param'] = json_decode($mediaItem['param'], true);
            }
            $res[$mediaItem['id']] = $mediaItem;
        }
        return $res;
    }

    /**
     * 处理媒体上传与关联数据数组
     * @param $data array 多余数据data
     * @param $primaryModuleName string 主模块名称 对应数组的key
     * @param $currentModel RelationModel 当前模块model
     * @return array
     * @throws \Exception
     */
    public function handleMediaItemUpload($data, $primaryModuleName, $currentModel)
    {
        $currentModuleLinkRelation = $currentModel->parserFilterModuleRelation(true);
        $relationIds = [];

        foreach ($data as $moduleCode => $item) {
            if (!isset($currentModuleLinkRelation[$moduleCode])) {
                continue;
            }
            if (!isset($this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']])) {
                continue;
            }

            if (!isset($item)) {
                continue;
            }
            $mediaIds = [];
            if (is_array($item) && is_many_dimension_array($item)) {
                foreach ($item as $mediaItem) {
                    if (empty($mediaItem)) {
                        continue;
                    }

                    if ($currentModuleLinkRelation[$moduleCode]['module_code'] === "media") {
                        $this->generateMediaData($mediaItem);
                    }
                    // 判定是否需要更新
                    if (isset($mediaItem[$this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getPk()])) {
                        $mediaIds[] = $mediaItem[$this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getPk()];
                        $mediaData = $this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->modifyItem($mediaItem);
                    } else {
                        $mediaData = $this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->addItem($mediaItem);
                    }
                    if (!$mediaData) {
                        // 跳过没有数据更新
                        if ($this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getErrorCode() !== \think\exception\ErrorCode::NO_DATA_HAS_BEEN_CHANGED) {
                            throw new LogicException($this->mediaMap[$currentModuleLinkRelation[$moduleCode]['module_code']]->getError(), ErrorCode::MEDIA_ADD_FAILURE);
                        }
                    } else {
                        $mediaIds[] = $mediaData['id'];
                    }
                }
            }
            $relationIds[$moduleCode] = join(',', array_unique($mediaIds));
        }

        // 把水平自定义关联数据回插入主表
        foreach ($relationIds as $field => $relationId) {
            $data[$primaryModuleName][$field] = $relationId;
        }

        return $data;
    }
}
